package se.webbzon.boltzmann.login;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;

import se.webbzon.oschi01.aes.HexInputStream;
import se.webbzon.oschi01.aes.HexOutputStream;
import se.webbzon.oschi01.aes.RijndaelBuilder;
import se.webbzon.oschi01.aes.RijndaelCipher;
import se.webbzon.oschi01.aes.RijndaelMatrix;
import se.webbzon.oschi01.hash.MD5;

public class HTTPLoginAccess extends LoginAccess {
	
	// The default HTTP timeout
	private static final int defaultTimeout = 10000;
	
	// The HTTP timeout of this instance
	private int timeout;
	
	/*============================================================
	Constructors
	============================================================*/

	public HTTPLoginAccess(String domain, String username, byte[] hashword) throws AccessException {
		super(domain, username, hashword);
		timeout = defaultTimeout;
		refresh();
	}

	/*============================================================
	Public Methods
	============================================================*/
	
	/** Sets the HTTP read timeout for this HTTPLoginAccess. The provided time is 
	 * must be given in milliseconds. **/
	public void setReadTimeout(int timeout) {
		this.timeout = timeout;
	}
	
	/** Returns the HTTP read timeout for this HTTPLoginAccess. The returned time is
	 * given in milliseconds. **/
	public int getReaderTimeout() {
		return timeout;
	}
	
	@Override public void refresh() throws AccessException {
		String response = null, message = null;
		HTTPCookie cookie = null;
		String urlStr = "http://" + getDomain() + "/session_login.php";
		URL url = null;
		try {
			url = new URL(urlStr);
		} catch (MalformedURLException e1) {
			throw new AccessException("Could not access the server \"" + urlStr + "\".");
		}
		
		// Retrieve the test string and the session id
		try (HTTPRequest req = new HTTPGetRequest(url); HTTPResponse res = req.send()) {
			response = res.readLine();
			message = response != null ? res.readLine() : null;
			cookie = res.cookies() > 0 ? res.getCookie(0) : null;
		} catch (IOException e) {
			throw new AccessException("An IOException occurred when accessing the server!",e);
		}
		
		if (message == null || cookie == null)
			throw new AccessException("Exception was thrown due to invalid server response!");
		
		byte[] sessionId = HexInputStream.fromHex(cookie.getValue());
			
		// Encrypt test vector
		RijndaelMatrix key = RijndaelMatrix.createKey(getHashword(), 0);
		RijndaelBuilder builder = new RijndaelBuilder();
		try {
			builder.writeBytes(message);
		} catch (IOException e) {
			throw new AccessException("An internal IOException occurred!",e);
		}
		byte[] data = new byte[builder.size()];
		builder.writeTo(data, 0);
		RijndaelMatrix[] matrices = RijndaelMatrix.fromBytes(data, 0, data.length);
		RijndaelCipher cipher = new RijndaelCipher();
		cipher.setKey(key);
		cipher.encrypt(matrices);
			
		// Create post response
		MD5 md5 = new MD5();
		md5.addString(getUsername(), Charset.forName("UTF-8"));
		String hashname = HexOutputStream.toHex(md5.sum());
		String testKey = HexOutputStream.toHex(data);
		
		// Login
		response = message = null;
		try (HTTPPostRequest req = new HTTPPostRequest(url,new HTTPCookie[] {cookie})) {
			req.addField("submitter_submit", "username=" + hashname + "&test=" + testKey);
			try (HTTPResponse res = req.send()) {
				response = res.readLine();
				message = response != null ? res.readLine() : null;
			}
		} catch (IOException e) {
			throw new AccessException("An IOException occurred when accessing the server!",e);
		}
			
		// Check server response
		if (response == null) {
			throw new AccessException("Exception was thrown due to invalid server response!");
		} else if (response.equals("NO")) {
			if (message == null)
				throw new AccessException("Access denied by server!");
			else
				throw new AccessException("Access denied by server!\n" + message);
		} else if (response.equals("OK")) {
			setSessionId(sessionId);
		} else
			throw new AccessException("Exception was thrown due to invalid server response!");
	}

}
